package com.zz.bid.aspect;

import com.zz.bid.annotation.RequiredAccess;
import com.zz.bid.exception.ForbiddenException;
import com.zz.bid.exception.LoginTimeoutException;
import com.zz.bid.service.UserService;
import com.zz.bid.to.ResponseWrapper;
import com.zz.bid.to.UserTO;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Objects;

@Aspect
@Component
@Slf4j
public class AccessCheckAspect {


    @Autowired
    private UserService userService;

    @Pointcut("@within(com.zz.bid.annotation.RequiredAccess)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public ResponseWrapper checkAccess(ProceedingJoinPoint proceedingJoinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        assert response != null;
        try {
            //获取注解类上的权限
            Annotation requiredAccessAnnotation = proceedingJoinPoint.getSignature().getDeclaringType().getDeclaredAnnotation(RequiredAccess.class);
            int[] requiredAccess = ((RequiredAccess) requiredAccessAnnotation).value();
            //校验权限
            checkAccess(requiredAccess, request);
            return (ResponseWrapper) proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
        } catch (ForbiddenException fe) {
            //拒绝访问
            response.setStatus(HttpStatus.FORBIDDEN.value());
            return ResponseWrapper.builder().message(fe.getMessage()).status(HttpStatus.FORBIDDEN.value()).path(request.getServletPath()).build();
        } catch (LoginTimeoutException le) {
            //token失效
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return ResponseWrapper.builder().message(le.getMessage()).status(HttpStatus.UNAUTHORIZED.value()).path(request.getServletPath()).build();
        } catch (Throwable t) {
            //其他错误
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            return ResponseWrapper.builder().message(t.getMessage()).status(HttpStatus.INTERNAL_SERVER_ERROR.value()).path(request.getServletPath()).build();
        }
    }

    private void checkAccess(int[] requiredAccess, HttpServletRequest request) throws LoginTimeoutException, ForbiddenException {
        String encodedToken = request.getHeader("token");
        if (StringUtils.isEmpty(encodedToken)) {
            log.error("token is null.");
            throw new LoginTimeoutException();
        }
        try {
            UserTO userTO = userService.getUserInfo(encodedToken);
            for (int required : requiredAccess) {
                if (Arrays.asList(userTO.getAccess()).contains(String.valueOf(required))) {
                    return;
                }
            }
        } catch (LoginTimeoutException le) {
            log.error(le.getMessage());
            throw le;
        }

        throw new ForbiddenException();
    }
}
